/*
Copyright [2012] [Maria Eugenia Malagamba]
Copyright [2012] [Leandro Pellegrino]
Copyright [2012] [Julia Gambuzzi]
Copyright [2012] [Ignacio Capodanno]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "Cliente.h"

Cliente::Cliente(string puerto, string direccionIp){

	this->puerto = puerto;
	this->direccionIp = direccionIp;
	this->socketClient = INVALID_SOCKET;
}


int Cliente::crearSocket(){

	if(WSAStartup(MAKEWORD(2,2), &this->wsa) != NO_ERROR)
	{
		cout << "ERROR: No se pudo iniciar WSAStartup \n";
	}

	struct addrinfo client_config;
	ZeroMemory(&client_config, sizeof(client_config));
	client_config.ai_family = AF_INET;
	client_config.ai_socktype = SOCK_STREAM;
	client_config.ai_protocol = IPPROTO_TCP;

	// Resuelve la direccion del cliente y el puerto.
	int resultado = getaddrinfo(this->direccionIp.c_str(), this->puerto.c_str(), &client_config, &(this->adressInfo));
	if ( resultado != 0 ) {
		//TODO: Loguear lo que ocurre y terminar el proceso.
		cout << "ERROR: Fallo al obtener la informacion de la direccion:" << resultado << "\n";
		return -1;
	}

	this->socketClient = socket(this->adressInfo->ai_family, this->adressInfo->ai_socktype, this->adressInfo->ai_protocol);
	if(this->socketClient == INVALID_SOCKET)
	{
		//TODO: Loguear lo que ocurre.
		cout << "ERROR: No se pudo crear el Socket\n";
		return -1;
	}

	return 0;
}

int Cliente::conectar(){

	// Conecto al servidor.
	int resultado = connect( this->socketClient, this->adressInfo->ai_addr, (int)this->adressInfo->ai_addrlen);
	if (resultado == SOCKET_ERROR) {
		cout << "No se pudo establecer la conexion\n";
		closesocket(this->socketClient);
		this->socketClient = INVALID_SOCKET;
	}

	freeaddrinfo(this->adressInfo);

	if (this->socketClient == INVALID_SOCKET) {
		cout << "Es imposible conectarse al servidor" << endl;
		return -1;
	}

	//Verifico que la conexion con el servidor se halla establecido, sino TIMEOUT
	fd_set pp;
	pp.fd_count = 1;
	pp.fd_array[0]= this->socketClient;

	timeval tv;
	tv.tv_sec = 10;
    tv.tv_usec = 0;

	int pip = select(0,&pp,NULL,NULL,&tv);
	if(pip <= 0){
		cout<<"No se pudo conectar al servidor, Timeout"<<endl;
		return -1;
	}	

	return resultado;
}

int Cliente::enviar(char mensaje[],int longitud){

	int resultado = send(this->socketClient,mensaje,longitud,0);

	if (resultado == SOCKET_ERROR) {
		cout << "Fallo el envio con error:" << WSAGetLastError() << endl;
		//closesocket(this->socketClient);
	}else{
		//cout << "Bytes enviados:" << resultado << endl;
	}
	return resultado;
}

int Cliente::recibir(char mensaje[],int longitud){

	int cantidadDatos = recv(this->socketClient, mensaje, longitud, 0);

	if(cantidadDatos > 0){
		//cout << "Mensaje recibido del servidor: "<<endl;// << mensaje << endl;

		return cantidadDatos;
	} else if (cantidadDatos <= 0){
		//TODO:Se cierra la conexion.
		cout << "Error al recibir el mensaje, corto la conexion "  << WSAGetLastError() << endl;
	}else{
		cout << "Error al recibir el mensaje"  << WSAGetLastError() << endl;
	}
	return -1;
}

void Cliente::cerrarConexion(){

	// se cierra la conexion.
	int resultado = shutdown(this->socketClient, SD_SEND);
	if (resultado == SOCKET_ERROR) {
		cout << "El cierre del socket termino con error:" << WSAGetLastError() << endl;
	}
	//Liberacion
	closesocket(this->socketClient);
	this->socketClient = INVALID_SOCKET;
	
	int pp = WSACleanup();
	cout <<"ERROR: "<<WSAGetLastError()<<endl;
}


int Cliente::recibirEntero(){

	int numero = -1;
	this->recibir((char*)&numero, sizeof(int));
	return numero;
}

int Cliente::enviarEntero(int numero){

	return this->enviar((char*)&numero, sizeof(int));
}

string Cliente::recibirString(){
	
	int tamBuffer = this->recibirEntero();
	char* cadena = new char[tamBuffer+1];
	this->recibir(cadena,tamBuffer);
	cadena[tamBuffer]= '\0';
	string toReturn(cadena);
	delete [] cadena;
	return toReturn;
}

int Cliente::enviarString(string aEnviar){
	
	int tamanioString = aEnviar.size();
	if(this->enviarEntero(tamanioString)){
		return this->enviar((char*)aEnviar.c_str(),tamanioString);
	}else{
		return -1;
	}
}

int Cliente::recibirArchivo(string pathCliente) {

	//variables
	int TAMANIOBUFFER =  1024;
	char* buffer = NULL;
	char* ultBuffer = NULL;
	int tamBuffer = 0;
	int numMensajes = 1;
	int ultMensaje = 0;

	//recibe el nombre del archivo que le van a enviar
	string pathArchivo = this->recibirString();
	pathCliente.append(pathArchivo);

	//Abrimos el archivo
	ofstream archivo;
	archivo.open(pathCliente.c_str(),ios::binary | ios::out);

	//recibo primero el tamanio del archivo que me van a enviar del servidor
	int tamanio =  this->recibirEntero();

	if (archivo.good()){

		//Obtenie el tamanio del archivo
		archivo.seekp(0, ios::beg);

		//si es mas grande que el buffer de transferencia lo dividimos
		if (tamanio > TAMANIOBUFFER){
			tamBuffer = TAMANIOBUFFER;
			numMensajes = tamanio / TAMANIOBUFFER;
			ultMensaje = tamanio % TAMANIOBUFFER; //resto de la division el ultimo buffer;

		}else {			
			//si no es mas grande que el buffer de transferencia es un solo msj
			tamBuffer= tamanio;
		}


	//Buffer para los mensajes
		buffer = new char[tamBuffer];

		int movimiento = 0;
		for( int i=0; i<numMensajes; i++){
			
			bool seRecibioMensaje = false;
			while(!seRecibioMensaje){
				//envio el buffer al cliente
				int resultado = this->recibir(buffer,tamBuffer);

				cout << "Recibi: "<<resultado<<endl;
				this->enviarEntero(resultado);
				if (resultado > 0){
					archivo.seekp(movimiento, ios::beg);
					archivo.write(buffer,tamBuffer);

					seRecibioMensaje = true;
				}
			}
			cout<<"Recibi el mensaje "<<i<<endl;
			movimiento += tamBuffer;

		}

		delete [] buffer;

		//Para el ultimo mensaje si no dio exacta la division
		if (ultMensaje != 0){
			ultBuffer = new char[ultMensaje];
			
			bool seRecibioMensaje = false;
			
			while(!seRecibioMensaje){
				//envio el buffer al cliente
				int resultado = this->recibir(ultBuffer,ultMensaje);

				cout << "Recibi: "<<resultado<<endl;
				this->enviarEntero(resultado);
				if (resultado > 0){
					archivo.seekp(movimiento, ios::beg);
					archivo.write(ultBuffer,ultMensaje);

					seRecibioMensaje = true;
				}
			}

			delete [] ultBuffer;

		}

		archivo.close();
		return 0;

	}else{
		cout<< "Error al crear el archivo"<<endl;
		return -1;

	}

}


Cliente::~Cliente(void){


}